package com.atkxp.sys.service.impl;

import com.alibaba.fastjson2.JSON;
import com.atkxp.sys.constant.Constants;
import com.atkxp.sys.entity.*;
import com.atkxp.sys.manager.AsyncManager;
import com.atkxp.sys.manager.factory.AsyncFactory;
import com.atkxp.sys.mapper.UserMapper;
import com.atkxp.sys.mapper.UserRoleMapper;
import com.atkxp.sys.service.IMenuService;
import com.atkxp.sys.service.IUserService;
import com.atkxp.sys.utils.JwtUtils;
import com.atkxp.sys.utils.RedisCache;
import com.atkxp.sys.utils.RedisUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author kxp
 * @since 2023-08-26
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    public BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    public RedisUtils redisUtils;

    @Autowired
    public JwtUtils jwtUtils;

    @Autowired
    public UserRoleMapper userRoleMapper;

    @Autowired
    public IMenuService menuService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private AuthenticationManager authenticationManager;
    @Override
    public Map<String, Object> login(User user) {

        Authentication authenticate = null;

        try {
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
            authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(user.getUsername(), Constants.LOGIN_FAIL, "user.password.not.match"));
                throw new AuthenticationCredentialsNotFoundException("登录账号或密码错误");
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(user.getUsername(), Constants.LOGIN_FAIL, e.getMessage()));
                throw new AccountExpiredException("jwt已过期，请重新登录");
            }
        }

        LoginUser login = (LoginUser) authenticate.getPrincipal();

        if(login != null) {

//            login.getUser().setPassword(null);
            String key = jwtUtils.createToken(login.getUser().getId());
            login.setToken(key);
//            redisUtils.setex(key, login, 1800);
            redisCache.setCacheObject(key, login, 1800, TimeUnit.SECONDS);

            Map<String, Object> data = new HashMap<>();
            data.put("token", key);

            AsyncManager.me().execute(AsyncFactory.recordLogininfor(user.getUsername(), Constants.LOGIN_SUCCESS, "user.login.success"));

            return data;
        }
        return null;
    }

    @Override
    public Map<String, Object> getUserInfo(String token) {
//        Object obj = redisUtils.get(token);
        LoginUser loginUser = redisCache.getCacheObject(token);
        if(loginUser != null) {
            User login = loginUser.getUser();
            Map<String, Object> data = new HashMap<>();
            data.put("name", login.getUsername());
            data.put("avatar", login.getAvatar());

            List<String> roleList = this.getBaseMapper().getRoleNamesByUserId(login.getId());
            data.put("roles", roleList);

            List<Menu> menuList = menuService.getMenuListByUserId(login.getId());
            data.put("menuList", menuList);
//            System.out.println(menuList);
            return data;
        }
        return null;
    }

    @Override
    public void logout(String token) {
        redisUtils.del(token);
    }

    @Override
    @Transactional
    public void addUser(User user) {
        this.baseMapper.insert(user);

        List<Integer> roleIdList = user.getRoleIdList();

        if(roleIdList != null) {
            for(Integer roleId: roleIdList) {
                userRoleMapper.insert(new UserRole(null, user.getId(), roleId));
            }
        }
    }

    @Override
    public User getUserById(Integer id) {
        User user = this.baseMapper.selectById(id);

        LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserRole::getUserId, id);
        List<UserRole> userRoleList = userRoleMapper.selectList(wrapper);

        List<Integer> roleIdList = userRoleList.stream().map(userRole -> {return userRole.getRoleId();}).collect(Collectors.toList());
        user.setRoleIdList(roleIdList);
        return user;
    }

    @Override
    @Transactional
    public void updateUser(User user) {
        this.baseMapper.updateById(user);

        LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserRole::getUserId, user.getId());
        userRoleMapper.delete(wrapper);

        List<Integer> roleIdList = user.getRoleIdList();

        if(roleIdList != null) {
            for(Integer roleId: roleIdList) {
                userRoleMapper.insert(new UserRole(null, user.getId(), roleId));
            }
        }

    }

    @Override
    @Transactional
    public void deleteUserById(Integer id) {
        this.baseMapper.deleteById(id);

        LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserRole::getUserId, id);
        userRoleMapper.delete(wrapper);
    }
}
